﻿using NetFrame.AbsClass;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace NetFrame {

    public class BaseServer {
        protected int port;//连接端口
        protected int maxConn;//最大连接数
        Semaphore maxConn_se;//连接信号量
        Socket Socket;
        protected int waitConn;//可挂起的连接数

        public AbsMessageCenter center;//上层消息处理中心

        public ObjPool<BaseToken> tokenPool;


        /// <summary>
        /// 初始化服务
        /// </summary>
        /// <param name="p">端口</param>
        /// <param name="conn">最大连接数</param>
        /// <param name="wait_num">挂起连接数</param>
        public BaseServer(int p, int conn, int wait_num = 100) {
            port = p;
            maxConn = conn;
            maxConn_se = new Semaphore(maxConn, maxConn);
            waitConn = wait_num;
        }

        /// <summary>
        /// 初始化函数
        /// </summary>
        protected virtual void Init() {
            tokenPool = new ObjPool<BaseToken>(maxConn);

            //初始化token(自定义),并把他压入对象池
            for (int i = 0; i < maxConn; i++) {
                BaseToken token = new BaseToken();

                token.read_args.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
                token.write_args.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);

                token.center = center;
                token.send_de += ProcessSend;
                token.close_de += CloseClientSocket;

                tokenPool.Push(token);
            }
        }

        /// <summary>
        /// 开始监听
        /// </summary>
        /// <param name="startTip">开始监听提示</param>
        public void Start(string startTip = "") {

            Init();

            try {
                //新建套接字
                Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                Socket.Bind(new IPEndPoint(IPAddress.Any, port));
                Socket.Listen(waitConn);
                Console.WriteLine(startTip);
                StartAccept(null);
            }
            catch (Exception e) {
                Console.WriteLine(e);
            }

        }

        void StartAccept(SocketAsyncEventArgs e) {
            //如果e为空，刚开始执行，e无连接，为e绑定完成事件
            if (e == null) {
                e = new SocketAsyncEventArgs();
                e.Completed += new EventHandler<SocketAsyncEventArgs>(Accept_Completed);
            }
            else {
                e.AcceptSocket = null;//如果不为空，证明e已经监听了一个连接，把连接清空，等待一个新的连接
            }

            //开始异步监听
            maxConn_se.WaitOne();//信号量减一

            try {
                bool result = Socket.AcceptAsync(e);

                //判断操作是否被挂起
                if (!result) {
                    //没有挂起，说明立即执行完毕,执行对应的监听处理操作
                    ProcessAccept(e);

                }//若挂起，等待连接，然后会执行Accept_Completed

            }
            catch (Exception ex) {
                Console.WriteLine(ex.ToString());
            }


        }

        /// <summary>
        /// 监听异步完成
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Accept_Completed(object sender, SocketAsyncEventArgs e) {
            //执行对应的监听处理操作
            ProcessAccept(e);
        }

        /// <summary>
        /// 处理监听
        /// </summary>
        /// <param name="e"></param>
        void ProcessAccept(SocketAsyncEventArgs e) {
            //从对象池取出读写 异步操作连接对象
            BaseToken token = tokenPool.Pop();

            //绑定 socket
            token.socket = e.AcceptSocket;

            //通知上层应用，有客户端连接
            center.OnClientConnected(token);

            try {
                //开始异步接收操作
                bool result = token.socket.ReceiveAsync(token.read_args);

                if (!result) {
                    ProcessReceive(token.read_args);
                }

                //循环利用e
                StartAccept(e);
            }
            catch (Exception ex) {
                Console.WriteLine(ex.ToString());
                CloseClientSocket(token, "接收数据失败");
            }


        }


        /// <summary>
        /// io异步完成（包括读和写）
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void IO_Completed(object sender, SocketAsyncEventArgs e) {
            // determine which type of operation just completed and call the associated handler
            switch (e.LastOperation) {
                case SocketAsyncOperation.Receive:
                    //如果该异步操作是接收
                    ProcessReceive(e);
                    break;
                case SocketAsyncOperation.Send:
                    //如果该异步操作是发送
                    ProcessSend(e);
                    break;
                default:
                    break;
                    //throw new ArgumentException("The last operation completed on the socket was not a receive or send");
            }

        }

        /// <summary>
        /// 处理接收
        /// </summary>
        /// <param name="e"></param>
        void ProcessReceive(SocketAsyncEventArgs e) {
            //取出token
            BaseToken token = (BaseToken)e.UserToken;

            //读取socket 接收到的字节数，存起来
            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success) {
                //接收的有值的数据长度就在 e.BytesTransferred
                //调用token中的接收方法，开始读取处理数据
                byte[] data = new byte[e.BytesTransferred];
                Buffer.BlockCopy(e.Buffer, 0, data, 0, data.Length);

               
                token.Recevice(data);

                try {
                    //循环异步接收操作
                    bool result = token.socket.ReceiveAsync(token.read_args);

                    if (!result) {
                        ProcessReceive(token.read_args);
                    }
                }
                catch (Exception ex) {
                    Console.WriteLine(ex.ToString());
                    CloseClientSocket(token, "接收数据失败");
                }

            }
            else {
                CloseClientSocket(token, "客户端断开连接");
            }

        }

        /// <summary>
        /// 处理发送
        /// </summary>
        /// <param name="e"></param>
        protected void ProcessSend(SocketAsyncEventArgs e) {
            //取出token
            BaseToken token = (BaseToken)e.UserToken;

            //读取socket 接收到的字节数，如果大于零而且socket状态为success
            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success) {
                token.Write();
            }
            else {
                CloseClientSocket(token, "客户端断开连接");
            }
        }

        /// <summary>
        /// 关闭socket
        /// </summary>
        /// <param name="token"></param>
        /// <param name="error"></param>
        protected void CloseClientSocket(BaseToken token, string error) {

            //通知上层，客户端断开连接，清空token数据，放入对象池中
            //关闭token时，外部不应继续调用，上锁
            lock (token) {
                center.OnClientClose(token, error);

                token.Close();
                tokenPool.Push(token);
                maxConn_se.Release();
            }
        }
    }
}
